package com.spagettikod.t437.simpledb;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.spagettikod.t437.T437;
import com.xerox.amazonws.sdb.Domain;
import com.xerox.amazonws.sdb.Item;
import com.xerox.amazonws.sdb.ItemAttribute;
import com.xerox.amazonws.sdb.ListDomainsResult;
import com.xerox.amazonws.sdb.QueryResult;
import com.xerox.amazonws.sdb.SDBException;

public class SimpleDBDomain
{
	private static final Log log = LogFactory.getLog(SimpleDBDomain.class);

	private LinkedList<String> allAttributes = new LinkedList<String>();

	private LinkedList<SimpleDBItem> items = new LinkedList<SimpleDBItem>();

	private String nextToken = null;

	protected Domain typicaDomain;

	public SimpleDBDomain(Domain domain)
	{
		this.typicaDomain = domain;
	}

	public static List<SimpleDBDomain> getAllDomains()
	{
		LinkedList<SimpleDBDomain> domainList = new LinkedList<SimpleDBDomain>();
		try
		{
			ListDomainsResult domainsResult = T437.getConfigurator().getSimpleDB().listDomains();
			if (domainsResult != null)
			{
				List<Domain> domains = domainsResult.getDomainList();
				for (Domain d : domains)
				{
					domainList.add(new SimpleDBDomain(d));
				}
			}
		}
		catch (SDBException e)
		{
			e.printStackTrace();
			T437.displayErrorMessage("AWS Communication Error", e.getLocalizedMessage());
		}

		return domainList;
	}

	private void addAttributeNames(SimpleDBItem item)
	{
		for (SimpleDBAttribute attr : item.getAttributes())
		{
			if (!allAttributes.contains(attr.getName()))
			{
				allAttributes.add(attr.getName());
			}
		}
	}

	public void addItem(SimpleDBItem item)
	{
		items.add(item);
		addAttributeNames(item);
	}

	public String getAttributeName(int index)
	{
		return allAttributes.get(index);
	}

	public int getItemCount()
	{
		return items.size();
	}

	public int getAttributeCount()
	{
		return allAttributes.size();
	}

	public boolean isEmpty()
	{
		return getItemCount() == 0;
	}

	public SimpleDBItem getItem(int index)
	{
		return items.get(index);
	}

	public void reset()
	{
		nextToken = null;
		allAttributes.clear();
		items.clear();
	}

	public boolean fetchData(String queryString, int rowsToFetch)
	{
		if (!isLastRowRead() || isEmpty())
		{
			if (log.isDebugEnabled())
			{

				log.debug("Fetching up to " + rowsToFetch + " rows");
			}
			try
			{
				T437.setBusyCursor(true);
				QueryResult qr;
				if (log.isDebugEnabled())
				{
					if (nextToken != null)
					{
						log.debug("Using next token");
					}
					else
					{
						log.debug("No next token");
					}
				}
				qr = typicaDomain.listItems(queryString, nextToken, rowsToFetch);
				nextToken = qr.getNextToken();

				List<Item> itemList = qr.getItemList();

				List<String> keys = new LinkedList<String>();

				if (log.isDebugEnabled())
				{
					log.debug("Fetched " + itemList.size() + " rows");
				}

				for (Item item : itemList)
				{
					keys.add(item.getIdentifier());
				}

				Map<String, List<ItemAttribute>> items = typicaDomain.getItemsAttributes(keys);
				for (String key : items.keySet())
				{
					List<ItemAttribute> attributes = items.get(key);

					SimpleDBItem item = new SimpleDBItem(key);
					for (ItemAttribute attr : attributes)
					{
						SimpleDBAttribute sdbAttr = item.getAttribute(attr.getName());
						if (sdbAttr == null)
						{
							sdbAttr = item.createAttribute(attr.getName());
						}
						sdbAttr.addValue(attr.getValue());
					}
					addItem(item);
				}
				if (log.isDebugEnabled())
				{
					log.debug("Has next token: " + (nextToken != null));
				}
			}
			catch (SDBException e)
			{
				log.error("Error while fetching data", e);
				T437.setBusyCursor(false);
				// TODO Should really be moved closer to the UI classes, throw
				// exception instead
				T437.displayErrorMessage("Error while fetching data", e.getLocalizedMessage());
			}
			finally
			{
				log.debug("Done fetching data");
				T437.setBusyCursor(false);
			}
		}
		else
		{
			log.trace("All rows already fetched");
			return false;
		}
		return true;
	}

	/**
	 * Check if all rows are read from this domain.
	 * 
	 * @return true if all rows are read, otherwise false.
	 */
	public boolean isLastRowRead()
	{
		return (nextToken == null);
	}

	public String getName()
	{
		return typicaDomain.getName();
	}

	@Override
	public String toString()
	{
		return typicaDomain.getName();
	}
}